#include "redis_adapter.h"

#include <map>
#include <boost/lexical_cast.hpp>

#include "base/logging.h"

using namespace std;
using namespace boost;
using namespace base;

namespace imserver {

RedisAdapter::RedisAdapter(RedisLocator * locator) : redis_exec_(locator) {
}

int RedisAdapter::LinkWaiterCustomerShop(int64_t waiter, int64_t customer, int64_t shop) {
  const int MAX_BUF_LEN = 512;
  char cmd_buf[MAX_BUF_LEN];
  cmd_buf[MAX_BUF_LEN - 1] = '\0';

  snprintf(cmd_buf, MAX_BUF_LEN - 1, "HSET im:waiter_customer_shop:%lld %lld %lld", waiter, customer, shop);
  for(int i = 0; i < 2; ++i) {
    ScopedReply reply(redis_exec_.Execute("", cmd_buf));
    if (reply) {
      LOG_INFO("redis LinkWaiterCustomerShop ok:" << cmd_buf << " retry=" << i);
      return 0;
    }
  }
  LOG_WARN("redis LinkWaiterCustomerShop error:" << cmd_buf);

  return -1;
}

int64_t RedisAdapter::GetLinkedShop(int64_t waiter, int64_t customer) {
  const int MAX_BUF_LEN = 256;
  char cmd_buf[MAX_BUF_LEN];
  cmd_buf[MAX_BUF_LEN - 1] = '\0';
  snprintf(cmd_buf, MAX_BUF_LEN - 1, "HGET im:waiter_customer_shop:%lld %lld", waiter, customer);

  for(int i = 0; i < 2; ++i) {
    ScopedReply reply(redis_exec_.Execute("", cmd_buf));
    if (reply) {
      if (reply->type == REDIS_REPLY_STRING) {
        LOG_INFO("redis GetLinkedShop ok:" << cmd_buf << " shop=" << reply->str << " retry=" << i);
        return boost::lexical_cast<int64_t>(reply->str);
      } else {
        LOG_INFO("redis GetLinkedShop " << cmd_buf << " no shop, retry=" << i);
        return 0;
      }
    }
  }
  LOG_WARN("redis GetLinkedShop err:" << cmd_buf);
  return -1;
}

/*
int RedisAdapter::AddSession(const int64_t uid, const std::string& session) {
  redisContext * context = redis_exec_.AllocContext("");
  if (context == NULL) {
    return -1;
  }

  redisAppendCommand(context, "HSET im:session:%lld %s %ld", uid, session.c_str(), time(NULL));
  redisAppendCommand(context, "EXPIRE im:session:%lld 600", uid); // 10分钟超时

  redisReply *reply = NULL;

  for(int i = 0; i < 2; ++i) {
    int rcode = redisGetReply(context, (void **)&reply);
    if (rcode != REDIS_OK || reply == NULL) {
      redis_exec_.ReleaseContext("", context, false);
      LOG_WARN("redis AddSession err : im:session:" << uid);
      return -1;
    }
    freeReplyObject(reply);
    reply = NULL;
  }

  LOG_DEBUG("AddSession ok : im:session:" << uid);
  redis_exec_.ReleaseContext("", context, true);
  return 0;
}

int RedisAdapter::LinkTempIdQid(const int64_t temp_id, const int64_t qid) {
  const int MAX_BUF_LEN = 256;
  char cmd_buf[MAX_BUF_LEN];
  cmd_buf[MAX_BUF_LEN - 1] = '\0';
  snprintf(cmd_buf, MAX_BUF_LEN - 1, "SETEX im:templogin:%lld 5184000 %lld ", temp_id, qid); // 60天有效
  for(int i = 0; i < 3; ++i) {
    ScopedReply reply(redis_exec_.Execute("", cmd_buf));
    if (reply) {
      LOG_INFO("redis LinkTempIdQid ok:" << cmd_buf);
      return 0;
    }
  }

  LOG_WARN("redis LinkTempIdQid error:" << cmd_buf);
  return -1;
}

int64_t RedisAdapter::GetQidByTempId(const int64_t temp_id) {
  const int MAX_BUF_LEN = 256;
  char cmd_buf[MAX_BUF_LEN];
  cmd_buf[MAX_BUF_LEN - 1] = '\0';
  snprintf(cmd_buf, MAX_BUF_LEN - 1, "GET im:templogin:%lld", temp_id);

  for(int i = 0; i < 2; ++i) {
    ScopedReply reply(redis_exec_.Execute("", cmd_buf));
    if (reply) {
      if (reply->type == REDIS_REPLY_STRING) {
        LOG_INFO("redis GetQidByTempId ok:" << cmd_buf << " qid=" << reply->str << " retry=" << i);
        return boost::lexical_cast<int64_t>(reply->str);
      } else {
        LOG_INFO("redis GetQidByTempId " << temp_id << " no qid, retry=" << i);
        return 0;
      }
    }
  }
  LOG_WARN("redis GetQidByTempId err:" << cmd_buf);
  return -1;
}

int RedisAdapter::RemoveSession(const int64_t uid, const std::string& session) {
  const int MAX_BUF_LEN = 512;
  char cmd_buf[MAX_BUF_LEN];
  cmd_buf[MAX_BUF_LEN - 1] = '\0';

  // snprintf(cmd_buf, MAX_BUF_LEN - 1, "HSET im:session:%lld %s 0", uid, session.c_str());
  snprintf(cmd_buf, MAX_BUF_LEN - 1, "HDEL im:session:%lld %s", uid, session.c_str());
  for(int i = 0; i < 2; ++i) {
    ScopedReply reply(redis_exec_.Execute("", cmd_buf));
    if (reply) {
      LOG_INFO("redis RemoveSession ok:" << cmd_buf << " retry=" << i);
      return 0;
    }
  }
  LOG_WARN("redis RemoveSession error:" << cmd_buf);

  return -1;
}

bool RedisAdapter::VerifySession(const int64_t uid, const std::string& session) {
  const int MAX_BUF_LEN = 128;
  char cmd_buf[MAX_BUF_LEN];
  cmd_buf[MAX_BUF_LEN - 1] = '\0';

  snprintf(cmd_buf, MAX_BUF_LEN - 1, "HEXISTS im:session:%lld %s", uid, session.c_str());

  LOG_DEBUG("VerifySession start " << cmd_buf);

  // 加强重试
  for(int i = 0; i < 4; ++i) {
    ScopedReply reply(redis_exec_.Execute("", cmd_buf));
    if (reply && reply->type == REDIS_REPLY_INTEGER) {
      LOG_INFO("VerifySession ok, " << cmd_buf << " res=" << reply->integer << " retry=" << i);
      return reply->integer == 1;
    } else {
      LOG_INFO("VerifySession to retry=" << i << " reply=" << (void*)reply);
    }
  }
  LOG_WARN("VerifySession fail, " << cmd_buf);
  return false;
}

int RedisAdapter::GetSessionList(SessionList * session_list, const int64_t uid) {
  const int MAX_BUF_LEN = 128;
  char cmd_buf[MAX_BUF_LEN];
  cmd_buf[MAX_BUF_LEN - 1] = '\0';

  snprintf(cmd_buf, MAX_BUF_LEN - 1, "HGETALL im:session:%lld", uid);

  ScopedReply reply(redis_exec_.Execute("", cmd_buf));
  if (!reply) {
    LOG_WARN("GetSessionList error:" << cmd_buf);
    return -1;
  }

  if (reply->type != REDIS_REPLY_ARRAY) {
    LOG_WARN(cmd_buf << " bad reply type=" << reply->type);
    return -1;
  }

  for(size_t i = 0; i < reply->elements; ++i) {
    redisReply * el = reply->element[i];
    if (i % 2 == 0) {
      session_list->push_back(Session());
      session_list->back().key = el->str;
    } else {
      int32_t ts = boost::lexical_cast<int32_t>(el->str);
      int32_t elapsed = time(NULL) - ts;
      if (elapsed > 300) { // 多副本、删除出错等的情况下，可能有残留session. 需要处理
        // TODO : remove stale session from redis
        LOG_INFO(uid << " stale session=" << session_list->back().key << " time_elapsed=" << elapsed);
        session_list->pop_back();
        // session_list->back().timestamp = ts;
      } else {
        LOG_DEBUG(uid << " active session=" << session_list->back().key << " time_elapsed=" << elapsed);
        session_list->back().timestamp = ts;
      }
    }
  }

  LOG_INFO("GetSessionList ok " << cmd_buf << ", session list size =" << session_list->size());
  return 0;
}

int RedisAdapter::GetOnlineUsers(const std::vector<int64_t> & ids, 
    std::map<int64_t, SessionType::type> * _return) {
  redisContext * context = redis_exec_.AllocContext("");
  if (context == NULL) {
    return -1;
  }

  for(size_t i = 0; i < ids.size(); ++i) {
    redisAppendCommand(context, "HGETALL im:session:%lld", ids[i]);
  }

  redisReply * reply = NULL;
  bool success = true;

  time_t now = time(NULL);
  for(size_t reply_count = 0; reply_count < ids.size(); ++reply_count) {
    int rcode = redisGetReply(context, (void **)&reply);

    if (reply == NULL) {
      LOG_WARN("GetOnlineUsers HGETALL null reply");
      success = false;
      break;
    }

    if (rcode == REDIS_ERR || reply->type != REDIS_REPLY_ARRAY) {
      LOG_WARN("GetOnlineUsers HGETALL bad reply");
      success = false;
      if (reply) {
        freeReplyObject(reply);
        reply = NULL;
      }
      break;
    }

    bool is_online = false; 
    for(size_t i = 0; i < reply->elements; ++i) {
      redisReply * el = reply->element[i];
      if (i % 2 == 0) {
        // 
      } else {
        int32_t ts = 0;
        try {
          ts = boost::lexical_cast<int32_t>(el->str);
        } catch (boost::bad_lexical_cast & e) {
          LOG_WARN("bad redis session timestamp, uid=" << ids[reply_count]);
          continue;
        }
        if (now - ts < 300) {
          is_online = true; 
          break;
        }
      }
    }
    // TODO: 支持其他在线类型
    if (is_online) {
      (*_return)[ids[reply_count]] = SessionType::WEB;
    }

    freeReplyObject(reply);
  }

  redis_exec_.ReleaseContext("", context, success);
  return 0;
}
*/

}

